<template>
  <div class="recall-test-container">
    <!-- 左侧容器：检索设置 -->
    <div class="left-panel">
      <div class="panel-header">
        <h3 class="panel-title">检索设置</h3>
      </div>
      <div class="panel-content">
        <a-card
                title="检索模式"
                :bordered="false"
                :headStyle="{ height: '20px', backgroundColor: '#eaecf0' }"
                :style="{ border: '1px solid blue' }"
                style="width: 100%;"
            >
                <div style="background-color: #fffbe6; padding: 8px; border-radius: 4px; border: 1px solid #ffe58f; margin-bottom: 16px;">
                  <span style="display: flex; align-items: center;">
                    <info-circle-outlined style="color: #faad14; margin-right: 8px;" />
                    <span>当前设置仅临时修改，只用于本次召回测试</span>
                  </span>
                </div>
                
                <div class="setting-section">
                    <div class="setting-title" style="text-align: left; margin-bottom: 12px;">检索模式</div>
                    <a-radio-group 
                        v-model:value="retrievalMode" 
                        button-style="solid" 
                        style="display: flex; width: 50%; border: 1px solid #d9d9d9; border-radius: 4px; overflow: hidden;"
                    >
                        <a-radio-button value="exact" style="flex: 1; text-align: center; border-right: 1px solid #d9d9d9; border-top: none; border-bottom: none; border-left: none;">
                            模糊匹配
                        </a-radio-button>
                        <a-radio-button value="vector" style="flex: 1; text-align: center; border: none;">
                            向量检索
                        </a-radio-button>
                    </a-radio-group>
                </div>
                
                <a-card 
                    :bordered="false" 
                    :headStyle="{ height: '20px', backgroundColor: '#eaecf0' }"
                    :style="{ marginTop: '16px', border: '1px solid #d9d9d9' }"
                    style="width: 100%;"
                >
                    <div style="display: flex; gap: 16px;">
                        <div style="flex: 1;">
                            <div class="setting-title" style="text-align: left; margin-bottom: 8px;">TopK</div>
                            <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
                                <a-input-number 
                                    v-model:value="retrievalCount" 
                                    :min="1" 
                                    :max="50" 
                                    style="width: 80px;" 
                                    @change="handleTopKChange"
                                />
                                <a-slider 
                                    v-model:value="retrievalCount" 
                                    :min="1" 
                                    :max="50" 
                                    style="flex: 1;" 
                                    @change="handleTopKChange"
                                />
                            </div>
                            <div style="font-size: 12px; color: rgba(0, 0, 0, 0.45);">
                                返回的最大结果数量
                            </div>
                        </div>
                        <div style="flex: 1;" v-if="retrievalMode === 'vector'">
                            <div class="setting-title" style="text-align: left; margin-bottom: 8px;">Score阈值</div>
                            <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
                                <a-input-number 
                                    v-model:value="similarityThresholdPercent" 
                                    :min="0" 
                                    :max="100" 
                                    :formatter="value => `${value}%`"
                                    :parser="value => value.replace('%', '')"
                                    style="width: 80px;" 
                                    @change="handleThresholdPercentChange"
                                />
                                <a-slider 
                                    v-model:value="similarityThreshold" 
                                    :min="0" 
                                    :max="1" 
                                    :step="0.01" 
                                    style="flex: 1;" 
                                    @change="handleThresholdChange"
                                />
                            </div>
                            <div style="font-size: 12px; color: rgba(0, 0, 0, 0.45); display: flex; justify-content: space-between;">
                                <span>低相关性</span>
                                <span>高相关性</span>
                            </div>
                        </div>
                    </div>
                </a-card>
                
                <!-- 模糊匹配检索维度卡片 -->
                <div v-if="retrievalMode === 'exact'" class="setting-title" style="text-align: left; margin-bottom: 12px; margin-top: 12px;">检索设置</div>
                <a-card 
                    v-if="retrievalMode === 'exact'"
                    :bordered="false" 
                    :headStyle="{ height: '20px', backgroundColor: '#eaecf0' }"
                    :style="{ marginTop: '16px', border: '1px solid #d9d9d9' }"
                    style="width: 100%;"
                >
                    <div class="setting-title" style="text-align: left; margin-bottom: 12px;">匹配字段</div>
                    <div style="margin-bottom: 20px;">
                        <div style="display: flex; gap: 12px; margin-bottom: 12px;">
                            <!-- 实体类型选择 -->
                            <div style="flex: 1;">
                                <div style="margin-bottom: 8px; font-size: 14px;">实体类型</div>
                                <a-select 
                                    v-model:value="selectedMatchEntityType" 
                                    placeholder="选择实体类型" 
                                    style="width: 100%"
                                    @change="onMatchEntityTypeChange"
                                >
                                    <a-select-option 
                                        v-for="type in entityTypes" 
                                        :key="type.value" 
                                        :value="type.value"
                                    >
                                        {{ type.label }}
                                    </a-select-option>
                                </a-select>
                            </div>
                            
                            <!-- 实体参数选择 -->
                            <div style="flex: 1;">
                                <div style="margin-bottom: 8px; font-size: 14px;">实体参数</div>
                                <a-select 
                                    v-model:value="selectedMatchParameter" 
                                    placeholder="选择实体参数" 
                                    style="width: 100%"
                                    :disabled="!selectedMatchEntityType || !entityParameters.length"
                                >
                                    <a-select-option 
                                        v-for="param in entityParameters" 
                                        :key="param.value" 
                                        :value="param.value"
                                    >
                                        {{ param.label }}
                                    </a-select-option>
                                </a-select>
                                <div v-if="!entityParameters.length && selectedMatchEntityType" style="font-size: 12px; color: #ff4d4f; margin-top: 4px;">
                                    当前实体类型没有可用参数
                                </div>
                            </div>
                        </div>
                    </div>

                    <div class="setting-title" style="text-align: left; margin-bottom: 12px;">匹配方式</div>
                    <a-radio-group v-model:value="fuzzyMatchType" style="width: 100%">
                        <div style="display: flex; flex-direction: column; gap: 8px;">
                            <a-radio value="contains">
                                <span>包含（检索结果包含查询文本的任意部分）</span>
                            </a-radio>
                            <a-radio value="startsWith">
                                <span>开头匹配（检索结果以查询文本开头）</span>
                            </a-radio>
                            <a-radio value="endsWith">
                                <span>结尾匹配（检索结果以查询文本结尾）</span>
                            </a-radio>
                            <a-radio value="exact">
                                <span>精确匹配（检索结果与查询文本完全一致）</span>
                            </a-radio>
                        </div>
                    </a-radio-group>
                </a-card>
                
                <!-- 返回类型卡片 -->
                <div class="setting-title" style="text-align: left; margin-bottom: 12px; margin-top: 12px;">返回类型</div>
                <a-card 
                    :bordered="false" 
                    :headStyle="{ height: '20px', backgroundColor: '#eaecf0' }"
                    :style="{ marginTop: '16px', border: '1px solid #d9d9d9' }"
                    style="width: 100%;"
                >
                    <div style="margin-bottom: 16px;">
                        <div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
                            <span>实体类型</span>
                            <a @click="selectAllEntityTypes" style="color: #1890ff; cursor: pointer;">{{ isAllEntityTypesSelected ? '取消全选' : '全选' }}</a>
                        </div>
                        <a-checkbox-group v-model:value="selectedEntityTypes" style="width: 100%">
                            <div style="display: flex; flex-wrap: wrap; gap: 12px; max-height: 120px; overflow-y: auto; padding: 8px; border: 1px solid #f0f0f0; border-radius: 4px;">
                                <a-checkbox 
                                    v-for="entityType in entityTypes" 
                                    :key="entityType.value" 
                                    :value="entityType.value"
                                    style="min-width: 90px; margin-right: 8px;"
                                >
                                    {{ entityType.label }}
                                </a-checkbox>
                            </div>
                        </a-checkbox-group>
                    </div>
                    
                    <div style="margin-bottom: 16px;">
                        <div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
                            <span>关系类型</span>
                            <a @click="selectAllRelationTypes" style="color: #1890ff; cursor: pointer;">{{ isAllRelationTypesSelected ? '取消全选' : '全选' }}</a>
                        </div>
                        <a-checkbox-group v-model:value="selectedRelationTypes" style="width: 100%">
                            <div style="display: flex; flex-wrap: wrap; gap: 12px; max-height: 120px; overflow-y: auto; padding: 8px; border: 1px solid #f0f0f0; border-radius: 4px;">
                                <a-checkbox 
                                    v-for="relationType in relationTypes" 
                                    :key="relationType.value" 
                                    :value="relationType.value"
                                    style="min-width: 90px; margin-right: 8px;"
                                >
                                    {{ relationType.label }}
                                </a-checkbox>
                            </div>
                        </a-checkbox-group>
                    </div>
                </a-card>
            </a-card>
            
            <!-- 模型设置卡片，仅在向量检索模式下显示 -->
            <a-card
                v-if="retrievalMode === 'vector'"
                title="模型设置"
                :bordered="false"
                :headStyle="{ height: '20px', backgroundColor: '#eaecf0' }"
                :style="{ border: '1px solid blue', marginTop: '16px' }"
                style="width: 100%;"
            >
                <div style="background-color: #fffbe6; padding: 8px; border-radius: 4px; border: 1px solid #ffe58f; margin-bottom: 16px;">
                  <span style="display: flex; align-items: center;">
                    <info-circle-outlined style="color: #faad14; margin-right: 8px;" />
                    <span>当前模型选择仅临时修改，只用于本次召回测试</span>
                  </span>
                </div>
                
                <div class="setting-section">
                    <div class="setting-title" style="text-align: left; margin-bottom: 12px;">Embedding模型</div>
                    <a-select 
                        v-model:value="embeddingModel" 
                        style="width: 100%;"
                        placeholder="选择Embedding模型"
                    >
                        <a-select-option value="bge-base-zh">BGE Base ZH</a-select-option>
                        <a-select-option value="bge-large-zh">BGE Large ZH</a-select-option>
                        <a-select-option value="bge-large-en">BGE Large EN</a-select-option>
                        <a-select-option value="text2vec">Text2Vec</a-select-option>
                        <a-select-option value="m3e-base">M3E Base</a-select-option>
                    </a-select>
                    <div style="font-size: 12px; color: rgba(0, 0, 0, 0.45); margin-top: 4px;">
                        用于将文本转换为向量的模型
                    </div>
                </div>
                
                <div v-if="hybridMode === 'rerank'" class="setting-section" style="margin-top: 20px;">
                    <div class="setting-title" style="text-align: left; margin-bottom: 12px;">Rerank模型</div>
                    <a-select 
                        v-model:value="rerankModel" 
                        style="width: 100%;"
                        placeholder="选择Rerank模型"
                    >
                        <a-select-option value="bge-reranker-base">BGE Reranker Base</a-select-option>
                        <a-select-option value="bge-reranker-large">BGE Reranker Large</a-select-option>
                        <a-select-option value="bce-reranker-base">BCE Reranker Base</a-select-option>
                        <a-select-option value="cohere-rerank">Cohere Rerank</a-select-option>
                    </a-select>
                    <div style="font-size: 12px; color: rgba(0, 0, 0, 0.45); margin-top: 4px;">
                        用于重排检索结果的模型，仅在Rerank模式下生效
                    </div>
                    
                </div>
            </a-card>
      </div>
    </div>
    
    <!-- 中间容器 -->
    <div class="middle-panel">
      <!-- 上方容器：召回测试 -->
      <div class="test-panel">
        <div class="panel-header">
          <h3 class="panel-title">召回测试</h3>
        </div>
        <div class="test-content">
          <a-textarea 
            v-model:value="queryText" 
            placeholder="请输入要测试的文本查询..." 
            :auto-size="{ minRows: 4, maxRows: 8 }"
            class="query-input"
          />
          <div class="action-bar">
            <a-button 
              type="primary" 
              @click="performRecall" 
              :loading="loading"
              :disabled="!canPerformRecall"
            >
              开始召回
            </a-button>
            <a-button @click="clearQuery">
              清空
            </a-button>
          </div>
          
          <!-- 历史记录部分 -->
          <div class="history-section">
            <div class="history-header">
              <div class="header-title">历史记录</div>
            </div>
            
            <div class="history-list" v-if="historyRecords.length > 0">
              <div 
                v-for="(item, index) in historyRecords" 
                :key="index" 
                class="history-item"
                @click="loadHistoryRecord(item)"
              >
                <div class="history-item-content">
                  <div class="history-query">{{ item.query }}</div>
                  <div class="history-meta">
                    <span>{{ item.time }}</span>
                    <span>{{ item.source || '本地知识库' }}</span>
                  </div>
                </div>
              </div>
            </div>
            
            <div class="empty-history" v-else>
              <a-empty description="暂无历史记录" />
            </div>
          </div>
        </div>
      </div>
    </div>
    
    <!-- 右侧容器：召回展示 -->
    <div class="right-panel">
      <div class="panel-header">
        <h3 class="panel-title">召回结果</h3>
        <div class="result-meta" v-if="recallResults.length > 0">
          共召回 {{ recallResults.length }} 条内容
        </div>
      </div>
      <div class="result-container">
        <div v-if="recallResults.length > 0" class="result-list">
          <div 
            v-for="(result, index) in recallResults" 
            :key="index" 
            class="result-item"
            :style="{ 
              backgroundColor: getEntityTypeColor(result.type), 
              borderColor: getEntityTypeBorderColor(result.type)
            }"
          >
            <div class="result-header">
              <div class="result-title-section">
                <a-tag :color="getEntityTypeTagColor(result.type)">{{ result.type === 'api' ? '接口' : result.type === 'param' ? '参数' : '关系' }}</a-tag>
                <div class="result-title">{{ result.title }}</div>
              </div>
              <div class="result-score">
                <a-tag :color="getScoreColor(result.score)">{{ (result.score * 100).toFixed(2) }}%</a-tag>
              </div>
            </div>
            
            <!-- 统一使用三列布局的表单 -->
            <div class="result-entity-details">
              <a-descriptions :column="2" size="small" bordered>
                <!-- API类型实体 -->
                <template v-if="result.type === 'api'">
                  <a-descriptions-item label="实体类型" :span="1">
                    <a-tag color="blue">接口</a-tag>
                  </a-descriptions-item>
                  <a-descriptions-item label="实体数据" :span="1">
                    <div>
                      <a-tag :color="getMethodColor(result.method)">{{ result.method }}</a-tag>
                      <span style="margin-left: 8px;">{{ result.path }}</span>
                    </div>
                  </a-descriptions-item>
                  
                  <!-- 请求依赖实体 -->
                  <a-descriptions-item v-if="result.params && result.params.request && result.params.request.length" label="请求依赖" :span="2">
                    <div class="entity-dependencies">
                      <a-table :dataSource="result.params.request" :pagination="false" size="small" :showHeader="true">
                        <a-table-column title="接口路径" dataIndex="api" />
                        <a-table-column title="方法" dataIndex="method">
                          <template #default="{ text }">
                            <a-tag :color="getMethodColor(text)">{{ text }}</a-tag>
                          </template>
                        </a-table-column>
                        <a-table-column title="说明" dataIndex="summary" />
                        <a-table-column v-if="hasDescriptionInRequests(result.params.request)" title="描述" dataIndex="description" />
                      </a-table>
                    </div>
                  </a-descriptions-item>
                  
                  <!-- 参数依赖实体 -->
                  <a-descriptions-item v-if="result.params && result.params.params && result.params.params.length" label="参数依赖" :span="2">
                    <div class="entity-dependencies">
                      <a-table :dataSource="result.params.params" :pagination="false" size="small" :showHeader="true">
                        <a-table-column v-for="(col, index) in getParamColumns(result.params.params)" 
                          :key="col.dataIndex" 
                          :title="col.title" 
                          :dataIndex="col.dataIndex">
                          <template v-if="col.dataIndex === 'required'" #default="{ text }">
                            <a-tag :color="text ? 'red' : 'green'">{{ text ? '是' : '否' }}</a-tag>
                          </template>
                        </a-table-column>
                      </a-table>
                    </div>
                  </a-descriptions-item>
                </template>
                
                <!-- 参数类型实体 -->
                <template v-if="result.type === 'param'">
                  <a-descriptions-item label="实体类型" :span="1">
                    <a-tag color="green">参数</a-tag>
                  </a-descriptions-item>
                  <a-descriptions-item label="实体数据" :span="1">
                    <div>
                      <span>{{ result.paramName }}</span>
                      <a-tag style="margin-left: 8px;">{{ result.dataType }}</a-tag>
                      <a-tag :color="result.isRequired ? 'red' : 'green'" style="margin-left: 8px;">
                        {{ result.isRequired ? '必填' : '可选' }}
                      </a-tag>
                    </div>
                  </a-descriptions-item>
                  
                  <a-descriptions-item label="参数描述" :span="2">
                    {{ result.description }}
                  </a-descriptions-item>
                  
                  <a-descriptions-item v-if="result.usedIn && result.usedIn.length" label="依赖实体" :span="2">
                    <div class="entity-dependencies">
                      <a-table :dataSource="result.usedIn.map(api => ({api}))" :pagination="false" size="small">
                        <a-table-column title="接口路径" dataIndex="api">
                          <template #default="{ text }">
                            <a-tag color="blue">{{ text }}</a-tag>
                          </template>
                        </a-table-column>
                      </a-table>
                    </div>
                  </a-descriptions-item>
                </template>
                
                <!-- 关系类型实体 -->
                <template v-if="result.type === 'relation'">
                  <a-descriptions-item label="实体类型" :span="1">
                    <a-tag color="orange">关系</a-tag>
                  </a-descriptions-item>
                  <a-descriptions-item label="实体数据" :span="1">
                    <div class="entity-relation">
                      <a-tag color="orange">{{ result.sourceEntity }}</a-tag>
                      <span class="dependency-arrow">→</span>
                      <a-tag color="orange">{{ result.targetEntity }}</a-tag>
                    </div>
                  </a-descriptions-item>
                  
                  <a-descriptions-item label="关系描述" :span="2">
                    {{ result.description }}
                  </a-descriptions-item>
                  
                  <a-descriptions-item v-if="result.apis && result.apis.length" label="依赖实体" :span="2">
                    <div class="entity-dependencies">
                      <a-table :dataSource="result.apis.map(api => ({api}))" :pagination="false" size="small">
                        <a-table-column title="接口路径" dataIndex="api">
                          <template #default="{ text }">
                            <a-tag color="blue">{{ text }}</a-tag>
                          </template>
                        </a-table-column>
                      </a-table>
                    </div>
                  </a-descriptions-item>
                </template>
              </a-descriptions>
            </div>
            
            <div class="result-footer">
              <div class="result-source">来源: {{ result.source }}</div>
              <div class="result-section">段落: {{ result.section }}</div>
            </div>
          </div>
        </div>
        <div v-else-if="loading" class="loading-results">
          <a-spin tip="正在召回中...">
          </a-spin>
        </div>
        <div v-else class="empty-results">
          <inbox-outlined style="font-size: 48px; color: #d9d9d9; margin-bottom: 16px;" />
          <p>暂无召回结果，请输入查询内容进行测试</p>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { 
  SaveOutlined, 
  DeleteOutlined, 
  ExperimentOutlined, 
  ClearOutlined,
  InboxOutlined,
  InfoCircleOutlined
} from '@ant-design/icons-vue'
import { message } from 'ant-design-vue'

// 检索设置
const retrievalModel = ref('bge-large-zh')
const retrievalCount = ref(8)
const similarityThreshold = ref(0.7)
const similarityThresholdPercent = ref(70) // 百分比形式的相似度阈值
const retrievalMode = ref('exact') // 默认为精确检索，可选值：exact(模糊匹配), vector(向量检索)
const hybridMode = ref('weight') // 默认为权重模式，可选值：weight(权重模式), rerank(Rerank模式)

// 模糊匹配设置
const fuzzySearchDimensions = ref(['title', 'content']) // 默认选中标题和内容
const fuzzyMatchType = ref('contains') // 默认为包含匹配

// 匹配字段设置
const selectedMatchEntityType = ref(''); // 当前选择的实体类型
const selectedMatchParameter = ref(''); // 当前选择的实体参数
const entityParameters = ref([]); // 当前实体类型的参数列表
const matchFields = ref([]); // 已添加的匹配字段

// 滑块标记
const sliderMarks = {
  1: '1',
  5: '5',
  10: '10',
  15: '15',
  20: '20'
}

const thresholdMarks = {
  0: '0',
  0.5: '0.5',
  1: '1'
}

// 查询文本
const queryText = ref('')
const loading = ref(false)

// 历史记录
const historyRecords = ref([
  {
    query: '/api/v1/users',
    time: '2023-06-15 14:30',
    count: 6,
    source: '模糊匹配'
  },
  {
    query: '查找所有依赖于用户服务的接口',
    time: '2023-06-14 11:25',
    count: 12,
    source: '向量检索'
  },
  {
    query: '接口调用关系分析',
    time: '2023-06-13 16:42',
    count: 8,
    source: '向量检索'
  },
  {
    query: 'POST /api/v1/orders',
    time: '2023-06-12 10:15',
    count: 5,
    source: '模糊匹配'
  },
  {
    query: 'tring',
    time: '2023-06-11 09:30',
    count: 15,
    source: '模糊匹配'
  },
  {
    query: '支付相关接口的依赖关系',
    time: '2023-06-10 15:20',
    count: 7,
    source: '向量检索'
  },
  {
    query: 'DELETE',
    time: '2023-06-09 11:05',
    count: 4,
    source: '模糊匹配'
  },
  {
    query: '订单服务与用户服务的接口依赖',
    time: '2023-06-08 16:40',
    count: 9,
    source: '向量检索'
  },
  {
    query: '显示所有接口参数关系',
    time: '2023-06-07 13:25',
    count: 18,
    source: '模糊匹配'
  },
  {
    query: 'API请求响应时间超过1秒的接口',
    time: '2023-06-06 10:30',
    count: 3,
    source: '模糊匹配'
  },
  {
    query: '所有与认证相关的接口',
    time: '2023-06-05 14:15',
    count: 6,
    source: '向量检索'
  },
  {
    query: '参数类型为数组的接口',
    time: '2023-06-04 09:45',
    count: 7,
    source: '模糊匹配'
  }
])

// 召回结果
const recallResults = ref([])

// 计算是否可以执行召回测试
const canPerformRecall = computed(() => {
  const hasQuery = queryText.value.trim().length > 0;
  const hasEntityTypes = selectedEntityTypes.value.length > 0;
  const hasRelationTypes = selectedRelationTypes.value.length > 0;
  
  // 也要验证匹配字段是否有值
  let hasMatchFieldsIfNeeded = true;
  
  // 只在模糊匹配模式下验证匹配字段
  if (retrievalMode.value === 'exact') {
    // 如果已经选择了实体类型，则需要选择实体参数
    if (selectedMatchEntityType.value) {
      hasMatchFieldsIfNeeded = selectedMatchParameter.value !== '';
    } else {
      // 如果没有选择实体类型，则这个条件是失败的
      hasMatchFieldsIfNeeded = false;
    }
  }
  
  return hasQuery && hasEntityTypes && hasRelationTypes && (retrievalMode.value === 'vector' || hasMatchFieldsIfNeeded);
});

// 执行召回
const performRecall = () => {
  if (!canPerformRecall.value) {
    // 提示用户填写必要信息
    let errorMessage = "无法执行召回测试，请检查以下内容：";
    const reasons = [];
    
    if (!queryText.value.trim()) {
      reasons.push("查询文本不能为空");
    }
    
    if (selectedEntityTypes.value.length === 0) {
      reasons.push("至少选择一种实体类型");
    }
    
    if (selectedRelationTypes.value.length === 0) {
      reasons.push("至少选择一种关系类型");
    }
    
    // 只在模糊匹配模式下检查匹配字段
    if (retrievalMode.value === 'exact') {
      if (!selectedMatchEntityType.value) {
        reasons.push("请选择匹配实体类型");
      } else if (!selectedMatchParameter.value) {
        reasons.push("请选择匹配实体参数");
      }
    }
    
    // 使用 AntDesign 的消息提示
    errorMessage += reasons.join("、");
    message.error(errorMessage);
    return;
  }
  
  loading.value = true;
  
  // 模拟召回延迟
  setTimeout(() => {
    // 模拟召回结果
    recallResults.value = [
      {
        title: 'GET /api/v1/users',
        content: '获取用户',
        source: 'users.controller.js',
        section: '路由定义',
        score: 0.92,
        type: 'api',
        method: 'GET',
        path: '/api/v1/users',
        auth: '用户管理权限或管理员权限',
        params: {
          request: [          
            { api: ' /api/v1/users', method: 'GET', summary: '获取用户列表' },
            { api: ' /api/v1/users/{id}', method: 'GET', summary: '获取单个用户详情', description: '获取单个用户详情' },
            { api: ' /api/v1/users', method: 'POST', summary: '创建用户', description: '创建用户' }
          ],
          params: [
            { name: 'page', type: 'number', required: false, description: '页码' },
            { name: 'size', type: 'number', required: false, description: '每页条数' },
            { name: 'query', type: 'string', required: false, description: '搜索关键词' }
          ]
        }
      },
      {
        title: 'POST /api/v1/users',
        content: '创建用户',
        source: 'users.controller.js',
        section: '控制器方法',
        score: 0.85,
        type: 'api',
        method: 'POST',
        path: '/api/v1/users',
        auth: '管理员权限',
        params: {
          request: [          
            { api: ' /api/v1/users', method: 'GET', summary: '获取用户列表' },
            { api: ' /api/v1/users/{id}', method: 'GET', summary: '获取单个用户详情', description: '获取单个用户详情' },
            { api: ' /api/v1/users', method: 'POST', summary: '创建用户', description: '创建用户' }
          ],
          params: [
            { name: 'page', type: 'number', required: false, description: '页码' },
            { name: 'size', type: 'number', required: false, description: '每页条数' },
            { name: 'query', type: 'string', required: false, description: '搜索关键词' }
          ]
        }
      },
      {
        title: 'GET /api/v1/users/{id}',
        content: '获取用户。',
        source: 'users.controller.js',
        section: '参数验证',
        score: 0.78,
        type: 'api',
        method: 'GET',
        path: '/api/v1/users/{id}',
        auth: '对应用户或管理员权限',
        params: {
          request: [          
            { api: ' /api/v1/users', method: 'GET', summary: '获取用户列表' },
            { api: ' /api/v1/users/{id}', method: 'GET', summary: '获取单个用户详情', description: '获取单个用户详情' },
            { api: ' /api/v1/users', method: 'POST', summary: '创建用户', description: '创建用户' }
          ],
          params: [
            { name: 'page', type: 'number', required: false, description: '页码' },
            { name: 'size', type: 'number', required: false, description: '每页条数' },
            { name: 'query', type: 'string', required: false, description: '搜索关键词' }
          ]
        }
      },
      {
        title: 'PUT /api/v1/users/{id}',
        content: '更新用户。',
        source: 'users.service.js',
        section: '业务逻辑',
        score: 0.75,
        type: 'api',
        method: 'PUT',
        path: '/api/v1/users/{id}',
        auth: '对应用户或管理员权限',
        params: {
          request: [          
            { api: ' /api/v1/users', method: 'GET', summary: '获取用户列表' },
            { api: ' /api/v1/users/{id}', method: 'GET', summary: '获取单个用户详情', description: '获取单个用户详情' },
            { api: ' /api/v1/users', method: 'POST', summary: '创建用户', description: '创建用户' }
          ],
          params: [
            { name: 'page', type: 'number', required: false, description: '页码' },
            { name: 'size', type: 'number', required: false, description: '每页条数' },
            { name: 'query', type: 'string', required: false, description: '搜索关键词' }
          ]
        }
      }
    ]
    
    // 添加到历史记录
    historyRecords.value.unshift({
      query: queryText.value,
      time: new Date().toLocaleString(),
      count: recallResults.value.length,
      source: retrievalMode.value === 'exact' ? '模糊匹配' : '向量检索'
    })
    
    loading.value = false
  }, 1500)
}

// 清空查询文本
const clearQuery = () => {
  queryText.value = ''
}

// 加载历史记录
const loadHistoryRecord = (item) => {
  queryText.value = item.query
  performRecall()
}

// 根据分数获取标签颜色
const getScoreColor = (score) => {
  if (score >= 0.8) return 'success'
  if (score >= 0.6) return 'processing'
  if (score >= 0.4) return 'warning'
  return 'default'
}

// 处理TopK变化
const handleTopKChange = (value) => {
  retrievalCount.value = value
  // 可以在这里添加TopK与阈值的关联逻辑
  // 例如：当TopK增大时，可以适当降低阈值
  if (value > 20 && similarityThreshold.value > 0.8) {
    similarityThreshold.value = 0.8
    similarityThresholdPercent.value = 80
  }
}

// 处理阈值变化（从滑块）
const handleThresholdChange = (value) => {
  similarityThreshold.value = value
  // 更新百分比形式的值
  similarityThresholdPercent.value = Math.round(value * 100)
}

// 处理阈值百分比变化（从输入框）
const handleThresholdPercentChange = (value) => {
  similarityThresholdPercent.value = value
  // 更新小数形式的值
  similarityThreshold.value = value / 100
}

// 权重设置
const semanticWeight = ref(50)
const keywordWeight = ref(50)

// 处理语义权重变化
const handleSemanticWeightChange = (value) => {
  semanticWeight.value = value
  // 自动计算关键词权重
  keywordWeight.value = 100 - value
}

// 模型设置
const embeddingModel = ref('bge-large-zh')
const rerankModel = ref('bge-reranker-base')
const initialRetrievalCount = ref(50)

// 实体类型列表
const entityTypes = [
  { value: 'api', label: '接口' },
  { value: 'param', label: '参数' }
];

// 关系类型列表
const relationTypes = [
  { value: 'dependsOn', label: '显示依赖' },
  { value: 'calls', label: '隐式依赖' },
  { value: 'imports', label: '参数必填' },
  { value: 'implements', label: '参数可填' },
];

// 从localStorage加载保存的数据
const loadSavedSelections = () => {
  try {
    // 加载已选择的实体类型
    const savedEntityTypes = localStorage.getItem('selectedEntityTypes');
    if (savedEntityTypes) {
      selectedEntityTypes.value = JSON.parse(savedEntityTypes);
    }
    
    // 加载已选择的关系类型
    const savedRelationTypes = localStorage.getItem('selectedRelationTypes');
    if (savedRelationTypes) {
      selectedRelationTypes.value = JSON.parse(savedRelationTypes);
    }
    
    // 加载已选择的匹配实体类型
    const savedMatchEntityType = localStorage.getItem('selectedMatchEntityType');
    if (savedMatchEntityType) {
      selectedMatchEntityType.value = savedMatchEntityType;
      // 触发参数列表加载
      onMatchEntityTypeChange(selectedMatchEntityType.value);
      
      // 加载已选择的匹配参数
      const savedMatchParameter = localStorage.getItem('selectedMatchParameter');
      if (savedMatchParameter) {
        selectedMatchParameter.value = savedMatchParameter;
      }
    }
    
    // 加载检索模式
    const savedRetrievalMode = localStorage.getItem('retrievalMode');
    if (savedRetrievalMode) {
      retrievalMode.value = savedRetrievalMode;
    }
    
    // 加载匹配方式
    const savedFuzzyMatchType = localStorage.getItem('fuzzyMatchType');
    if (savedFuzzyMatchType) {
      fuzzyMatchType.value = savedFuzzyMatchType;
    }
    
    // 加载TopK值
    const savedRetrievalCount = localStorage.getItem('retrievalCount');
    if (savedRetrievalCount) {
      retrievalCount.value = parseInt(savedRetrievalCount, 10);
    }
    
    message.success('已恢复上次的检索设置', 1);
  } catch (error) {
    console.error('加载保存的选择失败:', error);
    message.error('恢复上次设置失败');
  }
};

// 保存选择到localStorage
const saveSelections = () => {
  try {
    localStorage.setItem('selectedEntityTypes', JSON.stringify(selectedEntityTypes.value));
    localStorage.setItem('selectedRelationTypes', JSON.stringify(selectedRelationTypes.value));
    localStorage.setItem('selectedMatchEntityType', selectedMatchEntityType.value);
    localStorage.setItem('selectedMatchParameter', selectedMatchParameter.value);
    localStorage.setItem('retrievalMode', retrievalMode.value);
    localStorage.setItem('fuzzyMatchType', fuzzyMatchType.value);
    localStorage.setItem('retrievalCount', retrievalCount.value.toString());
  } catch (error) {
    console.error('保存选择失败:', error);
  }
};

// 已选择的实体类型和关系类型（默认全选）
const selectedEntityTypes = ref(entityTypes.map(item => item.value));
const selectedRelationTypes = ref(relationTypes.map(item => item.value));

// 监听选择变化并保存
watch(selectedEntityTypes, () => saveSelections(), { deep: true });
watch(selectedRelationTypes, () => saveSelections(), { deep: true });
watch(selectedMatchEntityType, () => saveSelections());
watch(selectedMatchParameter, () => saveSelections());
watch(retrievalMode, () => saveSelections());
watch(fuzzyMatchType, () => saveSelections());
watch(retrievalCount, () => saveSelections());

// 组件挂载时加载保存的选择
onMounted(() => {
  loadSavedSelections();
});

// 计算是否全部实体类型被选中
const isAllEntityTypesSelected = computed(() => {
  return selectedEntityTypes.value.length === entityTypes.length;
});

// 计算是否全部关系类型被选中
const isAllRelationTypesSelected = computed(() => {
  return selectedRelationTypes.value.length === relationTypes.length;
});

// 全选/取消全选实体类型
const selectAllEntityTypes = () => {
  if (isAllEntityTypesSelected.value) {
    selectedEntityTypes.value = [];
  } else {
    selectedEntityTypes.value = entityTypes.map(item => item.value);
  }
};

// 全选/取消全选关系类型
const selectAllRelationTypes = () => {
  if (isAllRelationTypesSelected.value) {
    selectedRelationTypes.value = [];
  } else {
    selectedRelationTypes.value = relationTypes.map(item => item.value);
  }
};

// 根据实体类型获取参数
const onMatchEntityTypeChange = (value) => {
  selectedMatchParameter.value = '';
  
  // 动态获取参数列表
  if (value === 'api') {
    entityParameters.value = [
      { value: 'url', label: 'URL' },
      { value: 'method', label: '请求方法' },
      { value: 'summary', label: '接口描述' },
      { value: 'path', label: '路径' }
    ];
  } else if (value === 'param') {
    entityParameters.value = [
      { value: 'type', label: '参数类型' },
      { value: 'required', label: '是否必填' },
      { value: 'description', label: '参数描述' },
      { value: 'defaultValue', label: '默认值' }
    ];
  } else {
    entityParameters.value = [];
  }
};

// 计算是否可以添加匹配字段
const canAddMatchField = computed(() => {
  return selectedMatchEntityType.value && selectedMatchParameter.value && 
    !matchFields.value.some(
      field => field.entityType === selectedMatchEntityType.value && 
              field.paramValue === selectedMatchParameter.value
    );
});

// 添加匹配字段
const addMatchField = () => {
  if (!canAddMatchField.value) return;
  
  const entityType = entityTypes.find(type => type.value === selectedMatchEntityType.value);
  const param = entityParameters.value.find(param => param.value === selectedMatchParameter.value);
  
  if (entityType && param) {
    matchFields.value.push({
      entityType: entityType.value,
      entityLabel: entityType.label,
      paramValue: param.value,
      paramLabel: param.label
    });
    
    // 清空选择，方便下次选择
    selectedMatchParameter.value = '';
  }
};

// 移除匹配字段
const removeMatchField = (index) => {
  matchFields.value.splice(index, 1);
};

// 清空所有匹配字段
const clearMatchFields = () => {
  matchFields.value = [];
  selectedMatchEntityType.value = '';
  selectedMatchParameter.value = '';
};

// 根据实体类型获取卡片颜色
const getEntityTypeColor = (type) => {
  switch(type) {
    case 'api':
      return '#e6f7ff'; // 浅蓝色背景，适合接口
    case 'param':
      return '#f6ffed'; // 浅绿色背景，适合参数
    case 'relation':
      return '#fff1f0'; // 浅红色背景，适合关系
    default:
      return '#f5f9ff'; // 默认背景色
  }
}

// 根据实体类型获取边框颜色
const getEntityTypeBorderColor = (type) => {
  switch(type) {
    case 'api':
      return '#91d5ff'; // 蓝色边框，适合接口
    case 'param':
      return '#b7eb8f'; // 绿色边框，适合参数
    case 'relation':
      return '#ffccc7'; // 红色边框，适合关系
    default:
      return '#e6f4ff'; // 默认边框色
  }
}

// 根据实体类型获取标签颜色
const getEntityTypeTagColor = (type) => {
  switch(type) {
    case 'api':
      return 'blue';
    case 'param':
      return 'green';
    case 'relation':
      return 'red';
    default:
      return 'default';
  }
}

// 获取HTTP方法对应的颜色
const getMethodColor = (method) => {
  switch(method) {
    case 'GET':
      return 'green';
    case 'POST':
      return 'blue';
    case 'PUT':
      return 'orange';
    case 'DELETE':
      return 'red';
    case 'PATCH':
      return 'purple';
    default:
      return 'default';
  }
}

// 获取参数表格列
const getParamColumns = (params) => {
  if (!params || !params.length) return [];
  
  // 获取第一个参数对象的所有键
  const firstParam = params[0];
  const columns = Object.keys(firstParam).map(key => {
    // 转换列名为更友好的显示
    let title = key;
    if (key === 'name') title = '参数名';
    else if (key === 'type') title = '类型';
    else if (key === 'required') title = '必填';
    else if (key === 'description') title = '描述';
    else if (key === 'in') title = '位置';
    else if (key === 'default') title = '默认值';
    
    return {
      title,
      dataIndex: key
    };
  });
  
  return columns;
};

// 检查请求数组中是否有描述字段
const hasDescriptionInRequests = (requests) => {
  if (!requests || !requests.length) return false;
  
  return requests.some(req => req.description && req.description.trim() !== '');
};
</script>

<style scoped>
.recall-test-container {
  margin-top: 0.5%;
  margin-left: 0.5%;
  margin-right: 0.5%;
  display: flex;
  height: 81vh;
  background-color: #f0f2f5;
  gap: 12px;
}

.left-panel,
.middle-panel,
.right-panel {
  background: white;
  border-radius: 8px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  max-height: 81vh;
}

.left-panel {
  width: 30%;
  min-width: 300px;
}

.middle-panel {
  width: 30%;
  display: flex;
  flex-direction: column;
}

.right-panel {
  width: 40%;
}

.panel-header {
  padding: 16px;
  border-bottom: 1px solid #f0f0f0;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.panel-title {
  font-size: 16px;
  font-weight: 500;
  margin: 0;
  color: rgba(0, 0, 0, 0.85);
}

.panel-content {
  flex: 1;
  padding: 16px;
  overflow-y: auto;
}

/* 左侧检索设置样式 */
.setting-section {
  margin-bottom: 20px;
}

.setting-title {
  margin-bottom: 8px;
  font-weight: 500;
  color: rgba(0, 0, 0, 0.85);
}

.save-settings-btn {
  margin-top: 16px;
}

/* 中间面板样式 */
.test-panel {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.test-content {
  padding: 16px;
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow-y: hidden;
}

.query-input {
  margin-bottom: 16px;
  border-radius: 4px;
}

.action-bar {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-bottom: 20px;
}

/* 历史记录样式 */
.history-section {
  margin-top: 20px;
  border-top: 1px solid #f0f0f0;
  padding-top: 16px;
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

.history-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
}

.header-title {
  font-size: 16px;
  font-weight: 500;
  color: rgba(0, 0, 0, 0.85);
}

.history-list {
  max-height: none;
  overflow-y: auto;
  margin-bottom: 12px;
  flex: 1;
  border: 1px solid #e8e8e8;
  border-radius: 4px;
  padding: 4px;
}

.history-item {
  padding: 10px 12px;
  border-bottom: 1px solid #f0f0f0;
  cursor: pointer;
  transition: all 0.3s;
  background-color: #fafafa;
  border-radius: 4px;
  margin-bottom: 8px;
  border: 1px solid #e9e9e9;
}

.history-item:last-child {
  margin-bottom: 0;
}

.history-item:hover {
  background-color: #f0f7ff;
}

.history-item-content {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.history-query {
  font-weight: 500;
  color: rgba(0, 0, 0, 0.85);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 60%;
}

.history-meta {
  display: flex;
  gap: 16px;
  font-size: 12px;
  color: rgba(0, 0, 0, 0.45);
  white-space: nowrap;
}

.empty-history {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100px;
  color: rgba(0, 0, 0, 0.45);
}

/* 右侧结果展示样式 */
.result-container {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
}

.result-meta {
  font-size: 14px;
  color: rgba(0, 0, 0, 0.45);
}

.result-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.result-item {
  background-color: #f5f9ff;
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
  border: 1px solid #e6f4ff;
  margin-bottom: 16px;
}

.result-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
}

.result-title-section {
  display: flex;
  align-items: center;
  gap: 8px;
}

.result-title {
  font-weight: 600;
  color: rgba(0, 0, 0, 0.85);
}

.result-content {
  margin-bottom: 12px;
  line-height: 1.6;
  color: rgba(0, 0, 0, 0.65);
}

.result-entity-details {
  margin-bottom: 16px;
}

.entity-dependencies {
  width: 100%;
}

.entity-dependency-item {
  display: flex;
  align-items: center;
  margin-bottom: 8px;
}

.entity-relation {
  display: flex;
  align-items: center;
}

.dependency-arrow {
  margin: 0 8px;
  color: rgba(0, 0, 0, 0.45);
  font-weight: bold;
}

.arrow-label {
  font-size: 12px;
  margin-bottom: 2px;
}

.no-dependencies {
  color: rgba(0, 0, 0, 0.45);
  font-style: italic;
}

.result-footer {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: rgba(0, 0, 0, 0.45);
  margin-top: 12px;
  padding-top: 8px;
  border-top: 1px dashed rgba(0, 0, 0, 0.1);
}

.empty-results,
.loading-results {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: rgba(0, 0, 0, 0.45);
  text-align: center;
  padding: 40px 0;
}

.loading-content {
  display: flex;
  flex-direction: column;
  align-items: center;
}
</style> 